Dart SDK gn 项目组织分析
Dart SDK 是一个庞大的项目,采用 GN 进行项目管理,关于 GN 的介绍,请参见《基于 gn 和 ninja 的构建系统》。
根目录 .gn
根目录下的 .gn
是入口:
-
buildconfig = "//build/config/BUILDCONFIG.gn"
:这行指定了构建配置文件的位置。//
表示从源代码树的根目录开始的路径。 -
secondary_source = "//build/secondary/"
:这行定义了一个次要的源代码根目录。这通常用于存放不能直接放在源代码树中的 GN 构建文件,例如第三方源代码树的构建文件。 -
script_executable = "python3"
:这行覆盖了默认的脚本执行程序,将其设置为 python3。这意味着所有的 GN 脚本都将使用 Python 3 来执行。
build/config/BUILDCONFIG.gn
该文件定义了 Dart SDK 的全局定义,分为几个部分。
PLATFORM SELECTION
这段代码主要用于设置和验证构建平台的相关参数。这里的参数包括操作系统(os)和 CPU 架构(cpu)。这些参数被分为三种类型:host(编译机器),target(目标构建平台),和 current(当前正在定义的配置)。
以下是这段代码的主要部分:
-
if (target_os == "") { target_os = host_os }
:如果没有指定目标操作系统(target_os),则默认使用编译机器的操作系统(host_os)。 -
assert(host_cpu != "")
和assert(target_cpu != "")
:这两行代码确保编译机器的 CPU 架构(host_cpu)和目标 CPU 架构(target_cpu)都已经被指定。 -
if (current_cpu == "") { current_cpu = target_cpu }
和if (current_os == "") { current_os = target_os }
:如果当前的 CPU 架构(current_cpu)或者当前的操作系统(current_os)没有被指定,那么默认使用目标 CPU 架构(target_cpu)或者目标操作系统(target_os)。
BUILD FLAGS
首先定义了一个全局变量 is_component_build
,并设置其值为 false
。用于指示是否进行组件构建。在组件构建中,每个组件(例如库或模块)都会被单独编译成自己的动态库,而不是全部链接到一个单一的可执行文件中。这可以加快增量构建的速度,因为修改一个组件只需要重新编译该组件,而不需要重新编译整个项目。
然而,有一条注释说明 Dart VM 不支持组件构建,但是一些依赖项的构建文件会检查这个标志。这意味着,尽管 Dart VM 本身不会进行组件构建,但是它的一些依赖项可能需要这个标志来决定它们自己的构建方式。
然后,有一个 declare_args()
函数,这个函数用于声明一系列的构建参数。这些参数包括:
is_debug
:是否为调试构建。is_release
:是否为发布构建。is_product
:是否为产品构建。is_clang
:是否使用 Clang 编译器进行编译。这通常用于配置警告。is_asan
:是否为 Address Sanitizer 编译,用于查找内存错误。is_lsan
:是否为 Leak Sanitizer 编译,用于查找内存泄漏。is_msan
:是否为 Memory Sanitizer 编译,用于查找未初始化的读取。is_tsan
:是否为 Thread Sanitizer 编译,用于查找线程错误。is_ubsan
:是否为 Undefined Behavior Sanitizer 编译,用于查找对未定义行为的依赖。is_qemu
:是否为在用户模式 QEMU 中执行的编译。is_fuchsia_tree
:是否在 Fuchsia 树中构建。这个参数应始终为false
,因为我们从不在 Fuchsia 树中构建。
这些参数可以在命令行中指定,如果没有指定,则使用 declare_args()
中定义的默认值。
OS DEFINITIONS
这段代码的主要目的是设置一些布尔值,以便在编写基于操作系统的条件时使用。这些布尔值包括 is_android
、is_chromeos
、is_ios
、is_win
、is_mac
、is_posix
和 is_linux
等。
代码首先检查 current_os
的值,这是一个变量,应该在此代码段之前定义,并设置为当前操作系统的名称。然后,根据 current_os
的值,代码设置一系列的布尔变量,以表示当前操作系统是否为特定的操作系统。
例如,如果 current_os
的值为 "win",那么 is_win
将被设置为 true
,而所有其他的 is_
变量将被设置为 false
。这意味着当前操作系统是 Windows。
这种方法的优点是,后续的代码可以使用这些 is_
变量来轻松检查当前操作系统,而无需每次都检查 current_os
的值。例如,如果你想要编写一些只在 Android 上运行的代码,你可以简单地检查 is_android
是否为 true
,而不是检查 current_os
是否等于 "android"。
BUILD OPTIONS
它检查了一些变量(is_clang
,is_asan
,is_lsan
,is_msan
,is_tsan
,is_ubsan
),这些变量通常用于指示是否启用特定的编译器或者编译选项。例如,is_clang
可能用于指示是否使用 Clang 编译器,is_asan
可能用于指示是否启用地址清理器(AddressSanitizer)等。
如果当前不是使用 Clang 编译器,但是启用了任何一种清理器(Sanitizer),那么它会强制使用 Clang 编译器。这是因为这些清理器都需要 Clang 编译器才能正常工作。
最后,它设置了 use_flutter_cxx
变量,如果启用了 MemorySanitizer(内存清理器,由 is_msan
控制)或者 ThreadSanitizer(线程清理器,由 is_tsan
控制),那么 use_flutter_cxx
将被设置为 true
。这可能意味着在这些情况下,构建系统将使用特定的编译选项或者编译器来编译 Flutter 的 C++ 代码。
TARGET DEFAULTS
这个文件主要定义了一些默认的构建配置,这些配置将应用于所有的构建目标。这些配置包括编译器设置、优化级别、符号设置、链接器设置等。
这个文件首先定义了一个名为 _native_compiler_configs
的列表,其中包含了一些通用的编译器配置。
然后,根据不同的条件(例如操作系统类型、是否使用特定的 C++ 库等),向 _native_compiler_configs
列表中添加了更多的配置。
接下来,文件定义了针对不同类型的构建目标(例如可执行文件、静态库、共享库等)的默认配置。这些配置主要是从 _native_compiler_configs
列表中继承的,但也可能添加了一些特定于目标类型的额外配置。
这段代码是在配置构建系统,特别是针对不同的目标平台和编译器设置。_native_compiler_configs
是一个列表,用于存储所有的配置,这些配置将用于生成本地可执行文件和库。以下是对各个部分的概述:
配置项 | 说明 |
---|---|
基本配置 | 这些是所有目标平台和编译器都需要的基本配置,包括编译器设置、C++版本、Clang堆栈对齐、ARM FPU和Thumb指令集、Chromium代码、默认包含目录、RTTI设置、运行时库等。 |
Flutter C++配置 | 如果使用了Flutter C++, 则会添加对应的配置。 |
Windows配置 | 如果目标平台是Windows,会添加一些特定的Windows配置,如SDK、Unicode、版本等。 |
POSIX配置 | 如果目标平台是POSIX兼容的(如Linux或MacOS),则会添加一些特定的POSIX配置。 |
Fuchsia配置 | 如果目标平台是Fuchsia,会添加一些特定的Fuchsia配置。 |
Linux/Mac/Android配置 | 如果目标平台是Linux、Mac或Android,会添加对应的SDK配置。 |
Clang配置 | 如果使用的编译器是Clang,会添加一些特定的Clang配置。 |
优化和调试检查 | 根据是否是调试或发布版本,会添加对应的优化和调试配置。 |
zlib配置 | 添加zlib需要的默认优化配置。 |
符号设置 | 添加符号设置配置。 |
Windows链接器设置 | 如果目标平台是Windows,会添加一些特定的Windows链接器配置。 |
可执行文件默认配置 | 根据目标平台,会添加对应的可执行文件配置。 |
静态库默认配置 | 设置静态库的默认配置。 |
共享库默认配置 | 根据目标平台,会添加对应的共享库配置。 |
源集默认配置 | 设置源集的默认配置。 |
组件默认配置 | 设置组件的默认配置。 |
最后,文件使用 set_defaults
函数为每种类型的构建目标设置了默认配置。这意味着,当你在其他地方定义一个新的构建目标时,这些默认配置将自动应用于新目标。
set_defaults
是一个GN构建系统的函数,它用于设置给定类型的所有目标的默认值。在这个文件中,set_defaults
被用于设置以下类型的目标的默认配置:
-
可执行文件("executable"):这个目标类型用于生成可执行文件。默认配置包括
_native_compiler_configs
中的所有配置,以及特定于平台的链接器配置(例如Windows、Mac、Linux或Android)。 -
静态库("static_library"):这个目标类型用于生成静态库。默认配置只包括
_native_compiler_configs
中的所有配置。 -
共享库("shared_library"):这个目标类型用于生成共享库或者在组件模式下的组件。默认配置包括
_native_compiler_configs
中的所有配置,以及一个共享库配置和特定于平台的链接器配置(例如Windows或Mac)。 -
源集("source_set"):这个目标类型用于编译一组源文件,但不链接它们。默认配置只包括
_native_compiler_configs
中的所有配置。 -
组件("component"):这个目标类型用于在非组件模式下的组件。默认配置只包括
_native_compiler_configs
中的所有配置。
这些默认配置可以在具体的目标定义中被覆盖或者添加新的配置。
TOOLCHAIN SETUP
这段代码主要做了两件事:
-
工具链设置:这部分代码设置了默认的工具链和主机工具链。工具链是一组用于编译和链接代码的工具集合,包括编译器、链接器等。主机工具链是用于编译运行在本地系统的代码的工具链。这部分代码根据不同的操作系统(如Windows、Android、Linux、Mac、Fuchsia)和编译器(如Clang)选择不同的工具链。
-
设置默认依赖:这部分代码为
executable
、loadable_module
和shared_library
目标设置了默认的依赖。如果no_default_deps
变量为真,那么不会添加任何标准依赖。否则,会根据是否使用use_flutter_cxx
和是否为Fuchsia系统来添加不同的依赖。
这段代码的主要目的是根据不同的环境和需求来配置构建系统,以便在不同的系统和环境中正确地编译和链接代码。
在GN构建系统中,set_default_toolchain
是一个内置函数,用于设置默认的工具链。工具链是一组用于编译和链接代码的工具集合,包括编译器、链接器等。
set_default_toolchain
函数接受一个字符串参数,该参数指定了默认工具链的标签。在你提供的代码中,这个标签通常是一个以"//build/toolchain/"开头的路径,后面跟着特定于操作系统、编译器和CPU架构的部分。
例如,set_default_toolchain("//build/toolchain/win:clang_$current_cpu")
会设置默认工具链为Windows系统下的Clang编译器,其中$current_cpu
是一个变量,表示当前的CPU架构。
这个函数的作用是,当GN生成Ninja构建文件时,如果没有明确指定工具链,那么就会使用这个默认工具链来编译和链接代码。
在GN(Generate Ninja)构建系统中,模板是一种特殊的函数,可以用来定义一组重复使用的构建规则。模板可以接受参数,并在模板体内部使用这些参数。模板的主要目的是减少重复的代码,使构建配置更加清晰和易于管理。
在你提供的代码中,template(_target_type)
定义了一个模板,模板的名称是_target_type
变量的值。这个模板接受一个参数target_name
,并在模板体内部定义了一个目标。目标的类型和名称都来自于模板的参数。
模板可以通过target
函数来调用。例如,如果你定义了一个名为executable
的模板,那么你可以通过executable("my_program")
来调用这个模板,创建一个名为my_program
的可执行目标。
在这个模板中,还使用了forward_variables_from
函数来转发调用者的变量,以及defined
函数来检查变量是否已定义。这些都是GN构建系统的内置函数,用于处理模板和目标的参数和变量。
定义了一个模板,用于为executable
、loadable_module
和shared_library
这三种类型的目标设置默认的依赖。这段代码的主要步骤如下:
-
使用
foreach
循环遍历三种目标类型:executable
、loadable_module
和shared_library
。 -
对于每种目标类型,定义一个模板。模板的名称就是目标类型的名称,例如
executable
。 -
在模板中,定义一个目标。目标的类型和名称都来自于模板的参数。
-
使用
forward_variables_from
函数将调用者的所有变量(除了no_default_deps
)转发到目标。 -
如果
deps
变量未定义,那么初始化它为一个空列表。 -
如果
no_default_deps
变量未定义,或者它的值为假,那么添加默认的依赖。具体的依赖取决于use_flutter_cxx
和is_fuchsia
两个变量的值。
这段代码的主要作用是,当你定义一个executable
、loadable_module
或shared_library
目标时,如果你没有明确指定依赖,那么这个模板会自动为你添加一些默认的依赖。这样可以简化构建配置,避免重复指定相同的依赖。
COMPONENT SETUP
这段代码主要做了以下几件事:
-
断言
is_component_build
为假。如果is_component_build
为真,那么会抛出错误并停止执行。这是因为代码注释指出,独立的 Dart VM 不应尝试进行组件构建。 -
设置
component_mode
为"source_set"
。这意味着构建模式被设置为源集(source set)模式。
在 GN 构建系统中,component_mode
是一个变量,它用于指定构建的模式。在这段代码中,component_mode
被设置为 "source_set"
,这意味着构建模式被设置为源集(source set)模式。
源集(source set)是 GN 构建系统中的一个概念,它表示一组源文件和它们的编译设置。当你在 GN 构建文件中定义一个源集,你需要指定源文件和编译设置,然后 GN 会生成相应的构建规则。
在这段代码中,component
模板创建了一个源集,并使用 invoker
提供的参数配置了这个源集。这意味着,当你使用这个模板时,你可以提供一组源文件和编译设置,然后这个模板会生成相应的构建规则。
总的来说,component_mode = "source_set"
这行代码的意思是,设置构建模式为源集模式,即构建规则将基于源文件和编译设置生成。
这个模板中的每个 if
语句都在检查 invoker
是否定义了特定的配置。如果定义了,那么就将该配置的值设置为 invoker
提供的值。这些配置包括但不限于编译器标志(如 cflags
,cflags_c
等)、依赖项(如 deps
)、库(如 libs
)等。
总的来说,这段代码定义了一个 GN 构建模板,该模板用于创建源集(source set)目标,并根据提供的参数配置该目标。
本文作者:Maeiee
本文链接:Dart SDK gn 项目组织分析
版权声明:如无特别声明,本文即为原创文章,版权归 Maeiee 所有,未经允许不得转载!
喜欢我文章的朋友请随缘打赏,鼓励我创作更多更好的作品!